package io.jnet.server;

import java.nio.channels.AsynchronousChannelGroup;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import com.jfireframework.baseutil.exception.JustThrowException;
import io.jnet.server.api.ServerChannelContextBuilder;
import io.jnet.server.api.ServerListener;
import io.jnet.server.util.DefaultServerListener;

public class AioServerBuilder
{
    public static enum ExecutorMode
    {
        FIX, CACHED
    }
    
    public static enum IoMode
    {
        /**
         * 简单的处理逻辑。只有io工作线程。在读取到一个消息后，在io回调线程内处理完业务逻辑放入写出堆栈。
         * 此种模式支持数组形式的BufStorage。
         */
        simple, //
        /**
         * 集中式处理。io线程和业务线程分开。io读取到的数据投递到业务线程中处理，处理完毕后投递到写出堆栈写出。
         * 由于是集中式处理，会出现响应消息的发出顺序和请求消息的到达顺序不是一一对应的情况。
         * 由于集中式处理，因此BufStorage类型只能选择队列类型。
         */
        concentration,
        /**
         * 任务与通道相绑定
         */
        TASK_ATTACH
    }
    
    private ServerListener              serverListener;
    private ServerChannelContextBuilder channelContextBuilder;
    // 服务器的启动端口
    private int                         port                 = -1;
    /**
     * 处理socket事件的起始线程数。如果线程池模式选择固定线程数模式的话，则这个数值就是线程数的值。如果线程池模式选择cache模式的话，则这个数值是初始线程数。
     */
    private int                         ioProcessorNum       = Runtime.getRuntime().availableProcessors();
    private int                         businessProcessorNum = Runtime.getRuntime().availableProcessors();
    private ExecutorMode                executorMode         = ExecutorMode.FIX;
    private IoMode                      ioMode               = IoMode.simple;
    private String                      bindIp               = "0.0.0.0";
    private ThreadFactory               threadFactory;
    private AsynchronousChannelGroup    channelGroup;
    
    public AioServer build()
    {
        try
        {
            if (threadFactory == null)
            {
                threadFactory = new ThreadFactory() {
                    int i = 1;
                    
                    @Override
                    public Thread newThread(Runnable r)
                    {
                        return new Thread(r, "服务端IO线程-" + (i++));
                    }
                };
            }
            if (channelGroup == null)
            {
                switch (executorMode)
                {
                    case FIX:
                        channelGroup = AsynchronousChannelGroup.withFixedThreadPool(ioProcessorNum, threadFactory);
                        break;
                    case CACHED:
                        channelGroup = AsynchronousChannelGroup.withCachedThreadPool(Executors.newCachedThreadPool(threadFactory), ioProcessorNum);
                        break;
                }
            }
            if (serverListener == null)
            {
                serverListener = new DefaultServerListener();
            }
            AioServer aioServer = new AioServer(businessProcessorNum, bindIp, port, channelGroup, ioMode, channelContextBuilder, serverListener);
            return aioServer;
        }
        catch (Throwable e)
        {
            throw new JustThrowException(e);
        }
    }
    
    public IoMode getIoMode()
    {
        return ioMode;
    }
    
    public void setIoMode(IoMode ioMode)
    {
        this.ioMode = ioMode;
    }
    
    public void setChannelContextBuilder(ServerChannelContextBuilder channelContextBuilder)
    {
        this.channelContextBuilder = channelContextBuilder;
    }
    
    public void setPort(int port)
    {
        this.port = port;
    }
    
    public void setBindIp(String bindIp)
    {
        this.bindIp = bindIp;
    }
    
    public void setThreadFactory(ThreadFactory threadFactory)
    {
        this.threadFactory = threadFactory;
    }
    
    public void setChannelGroup(AsynchronousChannelGroup channelGroup)
    {
        this.channelGroup = channelGroup;
    }
    
    public void setIoProcessorNum(int ioProcessorNum)
    {
        this.ioProcessorNum = ioProcessorNum;
    }
    
    public void setBusinessProcessorNum(int businessProcessorNum)
    {
        this.businessProcessorNum = businessProcessorNum;
    }
    
    public void setExecutorMode(ExecutorMode executorMode)
    {
        this.executorMode = executorMode;
    }
    
    public void setServerListener(ServerListener serverListener)
    {
        this.serverListener = serverListener;
    }
    
}
